import socket

# 1

# PLC 的 IP 地址和端口
plc_ip = '127.0.0.1'
plc_port = 1102

# 创建一个 TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    # 连接到 PLC
    sock.connect((plc_ip, plc_port))
    print(f"Connected to PLC at {plc_ip}:{plc_port}")
except Exception as e:
    print(f"Error connecting to PLC: {e}")
    sock.close()
    exit(1)



# 2


import struct

def build_s7_read_request(db_number, start_address, size):
    # # 协议头 (32 bytes)
    # protocol_header = b'\x32' + b'\x01' * 31  # 协议标识符和预留字段
    #
    # # 参数块 (12 bytes)
    # param_block = struct.pack('!HHBBBBBBI',
    #                           0x04,  # 参数块长度
    #                           0x12,  # 功能码（读取）
    #                           0x0A,  # DB 号
    #                           0x00,  # 预留
    #                           0x00,  # 预留
    #                           0x00,  # 预留
    #                           # 0x00,  # 预留
    #                           # 0x00,  # 预留
    #                           db_number,
    #                           start_address,
    #                           size)
    #
    # # 数据块 (0 bytes for read request)
    # data_block = b''
    #
    # # 完整的请求数据包
    # request_packet = protocol_header + param_block + data_block

    # 定义变量
    db_number = 1  # DB 号
    start_address = 0  # 起始地址
    size = 10  # 读取的字节数
    tsap_local = 0x0100  # 本地 TSAP
    tsap_remote = 0x0102  # 远程 TSAP

    # 构建 S7 参数块
    param_block = struct.pack('!HHBBBBBBBI',
                              0x04,  # 参数块长度
                              0x12,  # 功能码（读取）
                              db_number,
                              0x00,  # 预留
                              0x00,  # 预留
                              0x00,  # 预留
                              0x00,  # 预留
                              0x00,  # 预留
                              start_address,
                              size)

    # 构建 S7 数据块
    data_block = b''

    # 构建 S7 头部
    s7_header = struct.pack('!BBHHHH',
                            0x32,  # 协议 ID
                            0x01,  # ROSCTR (Job)
                            0x0000,  # Redundancy Identification
                            0x0000,  # PDU Reference
                            len(param_block),  # Parameter Length
                            len(data_block))  # Data Length

    # 构建 COTP 头部
    cotp_header = struct.pack('!BBBBBHH',
                              0x03,  # Length
                              0xF0,  # PDU Type (Data TPDU)
                              0x80,  # Last PDU
                              0x00,  # Send Sequence Number
                              0x00,  # Receive Sequence Number
                              tsap_local,  # Source TSAP
                              tsap_remote)  # Destination TSAP

    # 构建完整的 S7 请求数据包
    s7_request = s7_header + param_block + data_block
    complete_request = cotp_header + s7_request

    return complete_request

# 构建读取请求数据包
db_number = 1
start_address = 0
size = 10
request_packet = build_s7_read_request(db_number, start_address, size)

# 发送请求数据包
try:
    sock.sendall(request_packet)
    print("Request sent to PLC")
except Exception as e:
    print(f"Error sending request: {e}")
    sock.close()
    exit(1)


# 3

def parse_s7_response(response_packet):
    # 检查协议头
    if response_packet[:1] != b'\x32':
        raise ValueError("Invalid protocol header")

    # 解析参数块
    param_length, function_code, item_count, return_code, transport_size, length = struct.unpack('!HBBBHB',
                                                                                                 response_packet[32:42])

    # 解析数据块
    data = response_packet[42:42 + length]

    return {
        'param_length': param_length,
        'function_code': function_code,
        'item_count': item_count,
        'return_code': return_code,
        'transport_size': transport_size,
        'length': length,
        'data': data
    }


# 接收响应数据包
try:
    response_packet = sock.recv(1024)  # 假设响应数据包不会超过 1024 字节
    print(f"Received response from PLC: {response_packet.hex()}")

    # 解析响应数据包
    response = parse_s7_response(response_packet)
    print(f"Parsed response: {response}")

    # 打印读取的数据
    print(f"Read data: {response['data'].hex()}")
except Exception as e:
    print(f"Error receiving or parsing response: {e}")
finally:
    # 断开连接
    sock.close()
    print("Disconnected from PLC")



# import snap7
# client = snap7.client.Client()
# client.connect('127.0.0.1',0, 0,1102)
# d = bytearray(1)
# d[0] = 10
# client.write_area(snap7.types.Areas.DB, 0, 0, d)
# buffer = client.read_area(snap7.types.Areas.DB, 0, 0, 2)
# print(buffer)